package br3akr.DeadlyDinnerParty.Server.Actions;

import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.StringTokenizer;

import br3akr.DeadlyDinnerParty.Server.Engine.Server;
import br3akr.DeadlyDinnerParty.Server.Engine.Session;
import br3akr.DeadlyDinnerParty.Server.Exceptions.DeadlyDinnerPartyException;
import br3akr.DeadlyDinnerParty.Server.World.Area;
import br3akr.DeadlyDinnerParty.Server.World.Exit;
import br3akr.DeadlyDinnerParty.Server.World.GameCharacter;

public class SpeakAction extends Action {
    private ActionResponse actionResponse;
    public static final Set<String> keywords;
	static
	{
		Set<String>tempKeywords = new HashSet<String>();
		tempKeywords.add("Say");
		tempKeywords.add("'");
		tempKeywords.add("Scream");
		tempKeywords.add("sc");
		tempKeywords.add("Whisper");
		tempKeywords.add("Wh");
		tempKeywords.add("Announce");
		tempKeywords.add("An");
		tempKeywords.add("Shout");
		tempKeywords.add("Sh");
		tempKeywords.add("Knock");
		tempKeywords.add("Kn");
		tempKeywords.add("Emote");
		tempKeywords.add("Em");
		keywords = Collections.unmodifiableSet(tempKeywords);
	}
    
    public SpeakAction() {
        super("Speak", true);
    }
    
    public ActionResponse doAction(String action, Session session) throws DeadlyDinnerPartyException {
    	StringTokenizer tokenizedAction = new StringTokenizer(action);
    	String keyword = tokenizedAction.nextToken();
    	String param1 = null;
    	if (tokenizedAction.hasMoreTokens()) {
    		param1 = tokenizedAction.nextToken();
    	}
    	actionResponse = new ActionResponse();
    	actionResponse.setActingCharacter(session.getCharacter());
    	Area location;
    	Area announceLocation;
    	Set<GameCharacter> onlookers;
    	
		switch (keyword.toLowerCase()) {
			case "say":
			case "'":
				location = session.getCharacter().getLocation();
				onlookers = location.getAreaOnlookers(session.getCharacter(), null);
				String sayString = action.substring(keyword.length() + 1);
				if (session.getCharacter().isGagged()) {
					sayString = miffiffy(sayString);
				}
				actionResponse.addAffectedArea(session.getCharacter().getLocation());
				actionResponse.addOnlookers(onlookers, session.getCharacter().getName() + " says \"" + sayString + "\"");
				actionResponse.setActingCharacterDescription("You say \"" + sayString + "\"");
				for (GameCharacter gc : location.getListeners()) {
					if (!actionResponse.isOnlooker(gc)) {
						String spreakerDesc;
						Exit.ExitStatus doorStatus = location.getExitToArea(gc.getLocation()).getStatus();
						if (doorStatus == Exit.ExitStatus.BASHED || doorStatus == Exit.ExitStatus.OPEN) {
							spreakerDesc = session.getCharacter().getName() + " in " + location.getName() + " says ";
						} else {
							if (session.getCharacter().getGender() == GameCharacter.Gender.MALE) {
								spreakerDesc = "You hear a man say";
							} else {
								spreakerDesc = "You hear a woman say";
							}
						}
						actionResponse.addOnlooker(gc, spreakerDesc + " \"" + sayString + "\"");
					}
				}
				break;
			case "scream":
			case "sc":
				if (!session.getCharacter().isGagged()) {
					location = session.getCharacter().getLocation();
					for (Area curArea : Server.world.getAreas()) {
						onlookers = curArea.getAreaOnlookers(session.getCharacter(), null);
						actionResponse.addAffectedArea(curArea);
						if (curArea.equals(location)) {
							actionResponse.addOnlookers(onlookers, session.getCharacter().getName() + " screams!");
						} else {
							actionResponse.addOnlookers(onlookers, "You hear a scream from " + location.getName() + "!");
						}
					}
					
					actionResponse.setActingCharacterDescription("You scream!");
				} else {
					location = session.getCharacter().getLocation();
					for (Area curArea : Server.world.getAreas()) {
						onlookers = curArea.getAreaOnlookers(session.getCharacter(), null);
						actionResponse.addAffectedArea(curArea);
						if (curArea.equals(location)) {
							actionResponse.addOnlookers(onlookers, "Tries to scream, but the gag stops them.");
						}
					}
					
					actionResponse.setActingCharacterDescription("You try to scream, but the gag in your mouth stops you.");
				}
				break;
			case "shout":
			case "sh":
				if (!session.getCharacter().isGagged()) {
					location = session.getCharacter().getLocation();
					for (Area curArea : Server.world.getAreas()) {
						onlookers = curArea.getAreaOnlookers(session.getCharacter(), null);
						actionResponse.addAffectedArea(curArea);
						if (curArea.equals(location)) {
							actionResponse.addOnlookers(onlookers, session.getCharacter().getName() + " shouts \"" + action.substring(keyword.length() + 1) + "\"");
						} else {
							actionResponse.addOnlookers(onlookers, "Someone shouts \"" + action.substring(keyword.length() + 1) + "\"");
						}
					}
					
					actionResponse.setActingCharacterDescription("You shout \"" + action.substring(keyword.length()) + "\"");
				} else {
					location = session.getCharacter().getLocation();
					for (Area curArea : Server.world.getAreas()) {
						onlookers = curArea.getAreaOnlookers(session.getCharacter(), null);
						actionResponse.addAffectedArea(curArea);
						if (curArea.equals(location)) {
							actionResponse.addOnlookers(onlookers, session.getCharacter().getName() + " tries to shout, but can't due to the gag in their mouth.");
						}
					}
					
					actionResponse.setActingCharacterDescription("You try to shout, but can't due to the gag in your mouth.");
				}
				break;
			case "whisper":
			case "wh":
				location = session.getCharacter().getLocation();
				GameCharacter target = location.getCharacter(param1);
				Set<GameCharacter> targets = new HashSet<GameCharacter>();
				String whisperString = action.substring(action.indexOf(param1) + param1.length()).trim();
				
				if (session.getCharacter().isGagged()) {
					whisperString = miffiffy(whisperString);
				}
				
				if (target != null && !target.isHiddenFrom(session.getCharacter())) {
					targets.add(target);
					
					for (GameCharacter gc : session.getCharacter().getListeningCharacters()) {
						actionResponse.addOnlooker(gc, "You overhear " + session.getCharacter().getName() + " whisper " 
								+ "\"" + whisperString + "\" to " + target.getName());
					}
					
					for (GameCharacter gc : target.getListeningCharacters()) {
						actionResponse.addOnlooker(gc, "You overhear " + session.getCharacter().getName() + " whisper " 
								+ "\"" + whisperString + "\" to " + target.getName());
					}
					
					onlookers = location.getAreaOnlookers(session.getCharacter(), targets);
					actionResponse.addAffectedArea(session.getCharacter().getLocation());
					
					for (GameCharacter gc : onlookers) {
						if (actionResponse.isOnlooker(gc)) {
							onlookers.remove(gc);
						}
					}
					
					actionResponse.addOnlookers(onlookers, session.getCharacter().getName() + " whispers something to " + target.getName() + ".");
					actionResponse.addTargetCharacter(target);
					actionResponse.setActingCharacterDescription("You whisper \"" + whisperString + "\" to " + target.getName());
					actionResponse.setTargetCharacterDescription(target, session.getCharacter().getName() + " whispers \"" + whisperString + "\" to you.");
				} else {
					actionResponse.setActingCharacterDescription("There is no one here by the name of " + param1);
				}
				break;
			case "announce":
			case "an":
				if (!session.getCharacter().isGagged()) {
					location = session.getCharacter().getLocation();
			    	announceLocation = location.getAreaByExit(Exit.getDirection(param1));
			    	
			    	if (announceLocation != null) {
						onlookers = location.getAreaOnlookers(session.getCharacter(), null);
						actionResponse.addAffectedArea(location);
						actionResponse.addOnlookers(onlookers, session.getCharacter().getName() + " announces \"" + action.substring(action.indexOf(param1) + param1.length()).trim() + "\" to " + announceLocation.getName() + " in the " + Exit.getDirection(param1));
						
						onlookers = announceLocation.getAreaOnlookers(session.getCharacter(), null);
						actionResponse.addAffectedArea(announceLocation);
						Exit.ExitStatus doorStatus = location.getExitToArea(announceLocation).getStatus() ;
						if (doorStatus == Exit.ExitStatus.OPEN || doorStatus == Exit.ExitStatus.BASHED) {
							actionResponse.addOnlookers(onlookers, session.getCharacter().getName() + " announces \"" + action.substring(action.indexOf(param1) + param1.length()).trim() + "\" from " + location.getName() + " to the " + announceLocation.getExitToArea(location).getExitDirection(announceLocation) + ".");
						} else {
							actionResponse.addOnlookers(onlookers, "Someone announces \"" + action.substring(action.indexOf(param1) + param1.length()).trim() + "\" from " + location.getName() + " to the " + announceLocation.getExitToArea(location).getExitDirection(announceLocation) + ".");
						}
						actionResponse.setActingCharacterDescription("You announce \"" + action.substring(action.indexOf(param1) + param1.length()).trim() + "\" to " + announceLocation.getName() + " in the " + Exit.getDirection(param1));
			    	} else {
			    		actionResponse.setActingCharacterDescription("There isn't a room to the " + Exit.getDirection(param1));
			    	}
				} else {
					location = session.getCharacter().getLocation();
					onlookers = location.getAreaOnlookers(session.getCharacter(), null);
					actionResponse.addAffectedArea(location);
					actionResponse.addOnlookers(onlookers, session.getCharacter().getName() + " tries to shout something but can't due to the gag in their mouth.");
					actionResponse.setActingCharacterDescription("You try to shout, but can't due to the gag in your mouth.");
				}
				break;
			case "knock":
			case "kn":
				location = session.getCharacter().getLocation();
		    	announceLocation = location.getAreaByExit(Exit.getDirection(param1));
		    	
		    	if (announceLocation != null) {
		    		if (location.getExitStatus(Exit.getDirection(param1)) != Exit.ExitStatus.OPEN) {		    		
						onlookers = location.getAreaOnlookers(session.getCharacter(), null);
						actionResponse.addAffectedArea(location);
						actionResponse.addOnlookers(onlookers, session.getCharacter().getName() + " knocks on the door to " + announceLocation.getName() + " in the " + Exit.getDirection(param1));
						
						onlookers = announceLocation.getAreaOnlookers(session.getCharacter(), null);
						actionResponse.addAffectedArea(announceLocation);
						actionResponse.addOnlookers(onlookers, "Someone knocks on the door from " + location.getName() + " to the " + announceLocation.getExitToArea(location).getExitDirection(announceLocation) + ".");
						
						actionResponse.setActingCharacterDescription("You knock on the door to " + announceLocation.getName() + " in the " + Exit.getDirection(param1));
		    		} else {
		    			actionResponse.setActingCharacterDescription("The door to the " + Exit.getDirection(param1) + " is open.");
		    		}
		    	} else {
		    		actionResponse.setActingCharacterDescription("There isn't a room to the " + Exit.getDirection(param1));
		    	}
				break;
			case "emote":
			case "em":
				location = session.getCharacter().getLocation();
				onlookers = location.getAreaOnlookers(session.getCharacter(), null);
				actionResponse.addAffectedArea(session.getCharacter().getLocation());
				if (session.getCharacter().getHandcuffs() == null) {
					actionResponse.addOnlookers(onlookers, session.getCharacter().getName() + " " + action.substring(keyword.length() + 1));
					actionResponse.setActingCharacterDescription(session.getCharacter().getName() + " " + action.substring(keyword.length() + 1));
				} else {
					actionResponse.addOnlookers(onlookers, session.getCharacter().getName() + " struggles against their restraints.");
					actionResponse.setActingCharacterDescription("You struggle against your restraints. (you cannot emote while handcuffed)");
				}
				break;
			default:
				break;
		}
        return actionResponse;
    }
	
    public ActionResponse getActionResponse() {
		return actionResponse;
	}
    
    public Set<String> getKeywords() {
        return keywords;
    }
    
    private String miffiffy(String s) {
    	StringBuffer sb = new StringBuffer(s);
    	char[] ar = s.toCharArray();
    	Random r = new Random();
    	
    	for (int i = 0; i < ar.length; i++) {
    		if (Character.isLetter(ar[i])) {
    			if (r.nextInt(2) == 0) {
    				sb.setCharAt(i, 'm');
    			} else {
    				sb.setCharAt(i, 'f');
    			}
    		}
    	}
    	
    	return sb.toString();
    }
}